package com.css.common.core.utils;

import cn.hutool.core.date.LocalDateTimeUtil;
import org.apache.commons.lang3.StringUtils;

import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAdjusters;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

/**
 * 时间工具类
 */
public class DateUtils {
    public static final DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
    public static final DateTimeFormatter yyMMddHHmmssSSS = DateTimeFormatter.ofPattern("yyMMddHHmmssSSS");
    public static final DateTimeFormatter yyyy_MM_dd = DateTimeFormatter.ofPattern("yyyy-MM-dd");
    public static final DateTimeFormatter yyyy_MM_dd_HH_mm_ss = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    public static final String[] dateFormat = new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss", "yyyy年MM月dd日HH时mm分ss秒", "yyyy-MM-dd", "yyyy/MM/dd", "yy-MM-dd", "yy/MM/dd", "yyyy年MM月dd日", "HH:mm:ss", "yyyyMMddHHmmss", "yyyyMMdd", "yyyy.MM.dd", "yy.MM.dd", "yyyyMMddHHmmssSSS", "yyyy-MM-dd HH:mm:ss:SSS", "yyyy-MM-dd HH:mm:ss.SSS", "yyyy", "yyyyMM", "yyyyMMdd HH", "yyyyMMdd HH:mm", "yyyyMMdd HH:mm:ss"};
    private static final Map<String, DateTimeFormatter> dateFormatersCache = new ConcurrentHashMap();
    private static final ZoneId systemDefaultZoneId = ZoneId.systemDefault();

    /**
     * 时区 - 默认
     */
    public static final String TIME_ZONE_DEFAULT = "GMT+8";

    /**
     * 秒转换成毫秒
     */
    public static final long SECOND_MILLIS = 1000;

    public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";

    public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";

    public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";

    /**
     * 将 LocalDateTime 转换成 Date
     *
     * @param date LocalDateTime
     * @return LocalDateTime
     */
    public static Date of(LocalDateTime date) {
        if (date == null) {
            return null;
        }
        // 将此日期时间与时区相结合以创建 ZonedDateTime
        ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault());
        // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳
        Instant instant = zonedDateTime.toInstant();
        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
        return Date.from(instant);
    }

    /**
     * 将 Date 转换成 LocalDateTime
     *
     * @param date Date
     * @return LocalDateTime
     */
    public static LocalDateTime of(Date date) {
        if (date == null) {
            return null;
        }
        // 转为时间戳
        Instant instant = date.toInstant();
        // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间
        return LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    }

    public static Date addTime(Duration duration) {
        return new Date(System.currentTimeMillis() + duration.toMillis());
    }

    public static boolean isExpired(Date time) {
        return System.currentTimeMillis() > time.getTime();
    }

    public static boolean isExpired(LocalDateTime time) {
        LocalDateTime now = LocalDateTime.now();
        return now.isAfter(time);
    }

    public static long diff(Date endTime, Date startTime) {
        return endTime.getTime() - startTime.getTime();
    }

    /**
     * 创建指定时间
     *
     * @param year  年
     * @param mouth 月
     * @param day   日
     * @return 指定时间
     */
    public static Date buildTime(int year, int mouth, int day) {
        return buildTime(year, mouth, day, 0, 0, 0);
    }

    /**
     * 创建指定时间
     *
     * @param year   年
     * @param mouth  月
     * @param day    日
     * @param hour   小时
     * @param minute 分钟
     * @param second 秒
     * @return 指定时间
     */
    public static Date buildTime(int year, int mouth, int day,
                                 int hour, int minute, int second) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.MONTH, mouth - 1);
        calendar.set(Calendar.DAY_OF_MONTH, day);
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, minute);
        calendar.set(Calendar.SECOND, second);
        calendar.set(Calendar.MILLISECOND, 0); // 一般情况下，都是 0 毫秒
        return calendar.getTime();
    }

    public static Date max(Date a, Date b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.compareTo(b) > 0 ? a : b;
    }

    public static LocalDateTime max(LocalDateTime a, LocalDateTime b) {
        if (a == null) {
            return b;
        }
        if (b == null) {
            return a;
        }
        return a.isAfter(b) ? a : b;
    }

    /**
     * 计算当期时间相差的日期
     *
     * @param field  日历字段.<br/>eg:Calendar.MONTH,Calendar.DAY_OF_MONTH,<br/>Calendar.HOUR_OF_DAY等.
     * @param amount 相差的数值
     * @return 计算后的日志
     */
    public static Date addDate(int field, int amount) {
        return addDate(null, field, amount);
    }

    /**
     * 计算当期时间相差的日期
     *
     * @param date   设置时间
     * @param field  日历字段 例如说，{@link Calendar#DAY_OF_MONTH} 等
     * @param amount 相差的数值
     * @return 计算后的日志
     */
    public static Date addDate(Date date, int field, int amount) {
        if (amount == 0) {
            return date;
        }
        Calendar c = Calendar.getInstance();
        if (date != null) {
            c.setTime(date);
        }
        c.add(field, amount);
        return c.getTime();
    }

    /**
     * 是否今天
     *
     * @param date 日期
     * @return 是否
     */
    public static boolean isToday(LocalDateTime date) {
        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now());
    }

    /**
     * 是否昨天
     *
     * @param date 日期
     * @return 是否
     */
    public static boolean isYesterday(LocalDateTime date) {
        return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now().minusDays(1));
    }


    private static DateTimeFormatter getDateFormater(String format) {
        DateTimeFormatter dateTimeFormatter = dateFormatersCache.get(format);
        if (dateTimeFormatter == null) {
            dateTimeFormatter = DateTimeFormatter.ofPattern(format);
            dateFormatersCache.putIfAbsent(format, dateTimeFormatter);
        }

        return dateTimeFormatter;
    }

    public static Calendar getSystemCurrentTime() {
        final Calendar currentTime = Calendar.getInstance();
        return currentTime;
    }

    public static String getSystemCurrentTime(int format) {
        return toDateStrByFormatIndex(getSystemCurrentTime(), format);
    }

    public static String toDateStrByFormatIndex(Calendar date, int format) {
        return toDateStrByFormat(date, dateFormat[format]);
    }


    public static String toDateStrByFormat(Calendar date, String format) {
        return date == null ? null : getDateFormater(format).format(toLocalDateTime(date));
    }

    public static String toLocalDateTimeStrByFormat(LocalDateTime localDateTime, String format) {
        return localDateTime == null ? null :  toDateStrByFormat(Date.from( localDateTime.atZone(systemDefaultZoneId).toInstant()),format);
    }


    public static LocalDateTime toLocalDateTime(Calendar cal) {
        return LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId());
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDate.from(date.toInstant().atZone(systemDefaultZoneId).toLocalDate()).atStartOfDay();
    }

    public static LocalDateTime toLocalDateTime(String str,String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDate localDate = LocalDate.parse(str,formatter);
        return localDate.atStartOfDay();
    }

    public static LocalDateTime toLocalDateTime2(String str,String format) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        return LocalDateTime.parse(str,formatter);
    }

    public static Date toDate(String str,String format){
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDate localDate = LocalDate.parse(str, formatter);
        return Date.from(localDate.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date toDate(LocalDateTime localDateTime){
        return Date.from(localDateTime.atZone(systemDefaultZoneId).toInstant());
    }

    public static Date toDate(LocalDate localDate){
        return Date.from(localDate.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date LastMonthFirstDay(){
        LocalDate localDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).minusMonths(1);
        return Date.from(localDate.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date LastMonthLastDay(){
        LocalDate localDate = LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()).minusDays(1);
        return Date.from(localDate.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date nowYearFirstDay(){
        // 获取当前年份
        int year = Year.now().getValue();

        // 获取当前年份的第一天
        LocalDate firstDayOfYear = LocalDate.ofYearDay(year, 1);
        return Date.from(firstDayOfYear.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date nowYearLastDay(){
        // 获取当前年份
        int year = Year.now().getValue();

        // 获取当前年份的最后一天
        LocalDate lastDayOfYear = LocalDate.ofYearDay(year, 1).plusYears(1L).minusDays(1L);
        return Date.from(lastDayOfYear.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date LastQuarterFirstDay(){
        LocalDate currentDate = LocalDate.now();
        int currentMonth = currentDate.getMonthValue();
        int lastQuarterStartMonth = currentMonth - 3;
        int lastQuarterStartMonthAdjusted = lastQuarterStartMonth <= 0 ? lastQuarterStartMonth + 12 : lastQuarterStartMonth;
        LocalDate lastQuarterFirstDay = currentDate.withMonth(lastQuarterStartMonthAdjusted).withDayOfMonth(1);
        return Date.from(lastQuarterFirstDay.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static Date LastQuarterLastDay(){
        LocalDate currentDate = LocalDate.now();
        int currentMonthValue = currentDate.getMonthValue();
        int lastQuarterMonth = currentMonthValue - 3 < 1 ? currentMonthValue + 9 : currentMonthValue - 3;
        LocalDate lastQuarterEnd = LocalDate.of(currentDate.getYear(), Month.of(lastQuarterMonth), 1).plusMonths(1).minusDays(1);
        return Date.from(lastQuarterEnd.atStartOfDay().atZone(systemDefaultZoneId).toInstant());
    }

    public static String toDateStrByFormat(Date date, String format) {
        return date == null ? null : getDateFormater(format).format(toZonedDateTime(date));
    }

    public static ZonedDateTime toZonedDateTime(java.sql.Date date) {
        return toZonedDateTime(date.getTime());
    }

    public static ZonedDateTime toZonedDateTime(Date date) {
        return toZonedDateTime(date.getTime());
    }

    public static ZonedDateTime toZonedDateTime(long millseconds) {
        Instant instant = Instant.ofEpochMilli(millseconds);
        return instant.atZone(systemDefaultZoneId);
    }

    public static String toDateStrByFormatIndex(Date date, int format) {
        return toDateStrByFormat(date, dateFormat[format]);
    }

    public static String toDateStr(Calendar date) {
        return toDateStrByFormatIndex(date, 3);
    }

    public static String dateToString(Date date) {
        return toDateStrByFormatIndex(date, 3);
    }

    public static String dateToString(Date date, int format) {
        return toDateStrByFormatIndex(date, format);
    }

    public static Calendar parseDate(String dateStr) {
        if (dateStr != null && dateStr.trim().length() != 0 && !"null".equals(dateStr.toLowerCase())) {
            Date result = strToDate(dateStr);
            Calendar cal = Calendar.getInstance();
            cal.setTime(result);
            return cal;
        } else {
            return null;
        }
    }

    public static Calendar parseDate(String dateStr, String format) {
        if (dateStr != null && dateStr.trim().length() != 0 && !"null".equals(dateStr.toLowerCase())) {
            try {
                Calendar cal = Calendar.getInstance();
                cal.setTime((new SimpleDateFormat(format)).parse(dateStr));
                return cal;
            } catch (Exception var3) {
                return null;
            }
        } else {
            return null;
        }
    }

    public static Date strToDate(String dateStr) {
        return parse(dateStr);
    }

    private static Date parse(String dateStr) {
        if (StringUtils.isBlank(dateStr)) {
            return null;
        } else {
            int length = dateStr.length();
            if (StringUtils.isNumeric(dateStr)) {
                if (length == "yyyyMMddHHmmss".length()) {
                    return toDate(dateStr, "yyyyMMddHHmmss");
                }

                if (length == "yyyyMMddHHmmssSSS".length()) {
                    return toDate(dateStr, "yyyyMMddHHmmssSSS");
                }

                if (length == "yyyyMMdd".length()) {
                    return toDate(dateStr, "yyyyMMdd");
                }

                if (length == "HHmmss".length()) {
                    return toDate(dateStr, "HHmmss");
                }
            } else if (Pattern.matches("\\d{1,2}:\\d{1,2}(:\\d{1,2})?", dateStr)) {
                if (length == "HH:mm:ss".length()) {
                    return toDate(dateStr, "HH:mm:ss");
                }

                StringBuilder builder = new StringBuilder();
                builder.append(dateStr);
                builder.append(":00");
                return toDate(builder.toString(), "HH:mm:ss");
            }

            dateStr = normalize(dateStr);
            if (Pattern.matches("\\d{4}-\\d{1,2}-\\d{1,2}(\\s\\d{1,2}:\\d{1,2}(:\\d{1,2})?)?(.\\d{1,6})?", dateStr)) {
                int colonCount = dateStr.length();
                if ("yyyy-MM-dd".length() == dateStr.length()) {
                    return toDate(dateStr, "yyyy-MM-dd");
                }

                if ("yyyy-MM-dd HH:mm".length() == dateStr.length()) {
                    return toDate(dateStr, "yyyy-MM-dd HH:mm");
                }

                if ("yyyy-MM-dd HH:mm:ss".length() == dateStr.length()) {
                    return toDate(dateStr, "yyyy-MM-dd HH:mm:ss");
                }

                if ("yyyy-MM-dd HH:mm:ss.SSS".length() == dateStr.length()) {
                    return toDate(dateStr, "yyyy-MM-dd HH:mm:ss.SSS");
                }
            }

            throw new IllegalArgumentException("不支持的时间格式,参数值：" + dateStr + "不能转化为时间");
        }
    }

    private static String normalize(String dateStr) {
        String[] dateAndTime = dateStr.split("\\s+");
        int size = dateAndTime.length;
        if (size >= 1 && size <= 2) {
            StringBuilder builder = new StringBuilder();
            String datePart = dateAndTime[0].replaceAll("[/.年月]", "-");
            if (dateAndTime[0].endsWith("日")) {
                datePart = datePart.replaceAll("日", "");
            }

            builder.append(datePart);
            if (size == 2) {
                builder.append(' ');
                String timePart = dateAndTime[1].replaceAll("[时分]", ":");
                timePart = timePart.replaceAll("[,]", ".");
                timePart = removeSuffix(timePart, ":");
                if (timePart.endsWith("秒")) {
                    timePart = timePart.replaceAll("秒", "");
                } else {
                    timePart = timePart.replaceAll("秒", ".");
                }

                builder.append(timePart);
            }

            return builder.toString();
        } else {
            return dateStr;
        }
    }

    private static String removeSuffix(String str, String suffix) {
        if (!StringUtils.isBlank(str) && !StringUtils.isBlank(suffix)) {
            return str.endsWith(suffix) ? str.substring(str.length() - 1, str.length()) : str;
        } else {
            return str;
        }
    }

    //校验日期格式
    public static boolean checkDateFormat(String dateStr, DateTimeFormatter dtf) {
        if(BaseUtil.isNull(dateStr))return false;
        try{
            if(dateStr.length()<12){
                if(!dateStr.equals(LocalDate.parse(dateStr,dtf).format(dtf))) return false;
            }else{
                if(!dateStr.equals(LocalDateTime.parse(dateStr,dtf).format(dtf))) return false;
            }
        }catch(Exception e){
            return false;
        }
        return true;
    }
}
